﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CI.Models.Data;
using CI.Models.Production;
using CI.Models.Production.Interfaces;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CI.Models.Tests
{
    [TestClass]
    public class ManufacturingModelTests
    {

        [TestMethod]
        public void CanBuild_PI_RetailChain()
        {
            dynamic robotics = ProductionRepo.Get(9848);

            IJobModelManager piModelManager = new PIJobModelManager(ProductionRepo, robotics,
                                                                                  new PIModelOptions()
                                                                                      {
                                                                                          InputSource = InputSource.Retail,
                                                                                          OutputDestination =
                                                                                              InputSource.Retail
                                                                                      });


            // expected outcome is retail sale...
            var sale = piModelManager.ManagedChains.FirstOrDefault() as ISellItemsJob;

            Assert.IsNotNull(sale);

            // next step is a manufacturing...
            var tier4 = sale.GetChildJobs<IIndustryJob>().FirstOrDefault();

            Assert.IsNotNull(tier4);
        }

        [TestMethod]
        public void CanBuild_PI_ManufacturingChain()
        {
            Item robotics = ProductionRepo.Get(9848);

            IJobModelManager piModelManager = new PIJobModelManager(ProductionRepo, robotics,
                                                                                  new PIModelOptions
                                                                                      {MinimumManufacturingLevel = PIManufacturingLevel.Resource});

            var chainTop = piModelManager.ManagedChains.FirstOrDefault() as IIndustryJob;

            Assert.IsNotNull(chainTop);
            Assert.AreEqual(3, chainTop.Output.Quantity.Total);
            Assert.AreEqual(1, chainTop.Runs);
            Assert.AreEqual(robotics.TypeID, chainTop.Output.Item.TypeID);
            Assert.AreEqual(robotics.Schematic.OutputQuantity, chainTop.Output.Quantity.PerRun);

            IEnumerable<IIndustryProduct> matInputs = chainTop.Materials;

            Assert.AreEqual(2, matInputs.Count());
            Assert.AreEqual(60, chainTop.Time.PerRun.TotalMinutes);
            Assert.AreEqual(2, chainTop.ChildJobs.Count());
        }

        [TestMethod]
        public void BuildPIProductionJobFor_SetsUp_Tier3_Product_Correctly()
        {
            Item robotics = ProductionRepo.Get(9848);

            IIndustryJob job = PIJobModelManager.BuildPIProductionJobFor(robotics, new PIModelOptions() { MinimumManufacturingLevel = PIManufacturingLevel.Resource }, ProductionRepo, null);

            Assert.IsNotNull(job);

            Assert.AreEqual(1, job.Runs);
            Assert.AreEqual(1, job.Output.Quantity.Runs);
            Assert.AreEqual(3, job.Output.Quantity.PerRun);
            Assert.AreEqual(3, job.Output.Quantity.Total);
            Assert.AreEqual(1, job.Time.Runs);
            Assert.AreEqual(TimeSpan.FromSeconds(3600), job.Time.PerRun);
            Assert.AreEqual(TimeSpan.FromSeconds(3600), job.Time.Total);
        }

        [TestMethod]
        public void BuildPIProductionJobFor_SetsUp_Tier3_Product_Runs_Bindings_Correctly_FromJob()
        {
            Item robotics = ProductionRepo.Get(9848);

            IIndustryJob job = PIJobModelManager.BuildPIProductionJobFor(robotics, new PIModelOptions() { MinimumManufacturingLevel = PIManufacturingLevel.Resource }, ProductionRepo, null);

            Assert.IsNotNull(job);

            Assert.AreEqual(1, job.Runs);
            Assert.AreEqual(1, job.Output.Quantity.Runs);
            Assert.AreEqual(1, job.Time.Runs);
            Assert.AreEqual(1, job.Cost.Runs);

            job.Runs = 3;

            Assert.AreEqual(3, job.Runs);
            Assert.AreEqual(3, job.Output.Quantity.Runs);
            Assert.AreEqual(3, job.Time.Runs);
            Assert.AreEqual(3, job.Cost.Runs);
        }

        [TestMethod]
        public void BuildPIProductionJobFor_SetsUp_Tier3_Product_Requires_Correct_Materials()
        {
            Item robotics = ProductionRepo.Get(9848);


            IIndustryJob job = PIJobModelManager.BuildPIProductionJobFor(robotics, new PIModelOptions() { MinimumManufacturingLevel = PIManufacturingLevel.Resource },
                                                                    ProductionRepo, null);

            var mechanicalParts = job.Materials.Where(m => m.Item.TypeID == 3689).FirstOrDefault();
            var consumerElectronics = job.Materials.Where(m => m.Item.TypeID == 9836).FirstOrDefault();

            Assert.IsNotNull(mechanicalParts);
            Assert.IsNotNull(consumerElectronics);

            Assert.AreEqual(10, mechanicalParts.Quantity.PerRun);
            Assert.AreEqual(10, consumerElectronics.Quantity.PerRun);
        }

        [TestMethod]
        public void BuildPIProductionJobFor_SetsUp_Tier3_Product_Has_Correct_ChildJobs()
        {
            Item robotics = ProductionRepo.Get(9848);

            IIndustryJob job = PIJobModelManager.BuildPIProductionJobFor(robotics, new PIModelOptions() { MinimumManufacturingLevel = PIManufacturingLevel.Resource },
                                                                    ProductionRepo, null);

            var mpJob = job.GetChildJobs<IIndustryJob>().Where(c => c.Output.Item.TypeID == 3689).FirstOrDefault();
            var ceJob = job.GetChildJobs<IIndustryJob>().Where(c => c.Output.Item.TypeID == 9836).FirstOrDefault();

            Assert.IsNotNull(mpJob);
            Assert.AreEqual(2, mpJob.Runs);
            Assert.IsNotNull(ceJob);
            Assert.AreEqual(2, ceJob.Runs);
        }

        [TestMethod]
        public void BuildPIProductionJobFor_SetsUp_Tier3_Product_Has_Correct_ChildJobs_RunsChange_Propogates_Correctly()
        {
            Item robotics = ProductionRepo.Get(9848);

            IIndustryJob job = PIJobModelManager.BuildPIProductionJobFor(robotics, new PIModelOptions() { MinimumManufacturingLevel = PIManufacturingLevel.Resource },
                                                                    ProductionRepo, null);

            var mpJob = job.GetChildJobs<IIndustryJob>().Where(c => c.Output.Item.TypeID == 3689).FirstOrDefault();
            var ceJob = job.GetChildJobs<IIndustryJob>().Where(c => c.Output.Item.TypeID == 9836).FirstOrDefault();

            Assert.IsNotNull(mpJob);
            Assert.AreEqual(2, mpJob.Runs);
            Assert.IsNotNull(ceJob);
            Assert.AreEqual(2, ceJob.Runs);

            job.Runs = 11;
            Assert.AreEqual(33, job.Output.Quantity.Total);
            Assert.AreEqual(22, mpJob.Runs);
            Assert.AreEqual(22, ceJob.Runs);
            Assert.AreEqual(110, ceJob.Output.Quantity.Total);
            Assert.AreEqual(110, mpJob.Output.Quantity.Total);
        }

        [TestMethod]
        public void BuildPIProductionJobFor_SetsUp_Tier3_Product_Requires_Correct_Materials_Change_NumberOfRuns()
        {
            Item robotics = ProductionRepo.Get(9848);

            IIndustryJob job = PIJobModelManager.BuildPIProductionJobFor(robotics, new PIModelOptions() { MinimumManufacturingLevel = PIManufacturingLevel.Resource },
                                                                    ProductionRepo, null);

            job.Runs = 10;

            var mechanicalParts = job.Materials.Where(m => m.Item.TypeID == 3689).FirstOrDefault();
            var consumerElectronics = job.Materials.Where(m => m.Item.TypeID == 9836).FirstOrDefault();

            Assert.IsNotNull(mechanicalParts);
            Assert.IsNotNull(consumerElectronics);

            Assert.AreEqual(10, mechanicalParts.Quantity.PerRun);
            Assert.AreEqual(10, consumerElectronics.Quantity.PerRun);

            Assert.AreEqual(100, mechanicalParts.Quantity.Total);
            Assert.AreEqual(100, consumerElectronics.Quantity.Total);
        }

        [TestMethod]
        public void BuildPIProductionJobFor_SetsUp_Tier3_Product_Runs_Bindings_Correctly_FromJob_To_Materials()
        {
            Item robotics = ProductionRepo.Get(9848);

            IIndustryJob job = PIJobModelManager.BuildPIProductionJobFor(robotics, new PIModelOptions() { MinimumManufacturingLevel = PIManufacturingLevel.Resource }, ProductionRepo, null);

            Assert.IsNotNull(job);

            Assert.AreEqual(1, job.Runs);
            foreach(var material in job.Materials)
                Assert.AreEqual(1, material.Quantity.Runs);

            job.Runs = 3;

            Assert.AreEqual(3, job.Runs);
            foreach (var material in job.Materials)
                Assert.AreEqual(3, material.Quantity.Runs);
        }

        [TestMethod]
        public void BuildPIProductionJobFor_SetsUp_Tier3_Product_Runs_Bindings_Correctly_FromOutput()
        {
            Item robotics = ProductionRepo.Get(9848);

            IIndustryJob job = PIJobModelManager.BuildPIProductionJobFor(robotics, new PIModelOptions() { MinimumManufacturingLevel = PIManufacturingLevel.Resource }, ProductionRepo, null);

            Assert.IsNotNull(job);

            Assert.AreEqual(1, job.Runs);
            Assert.AreEqual(1, job.Output.Quantity.Runs);
            Assert.AreEqual(1, job.Time.Runs);
            Assert.AreEqual(1, job.Cost.Runs);

            job.Output.Quantity.Runs = 3;

            Assert.AreEqual(3, job.Runs);
            Assert.AreEqual(3, job.Output.Quantity.Runs);
            Assert.AreEqual(3, job.Time.Runs);
            Assert.AreEqual(3, job.Cost.Runs);
        }

        public static IStaticDataRepository<Item> ProductionRepo
        {
            get
            {
                return StaticData.ProductionRepo;
            }
        }

        [TestMethod]
        public void CanBuildJob_For_All_PI_Items()
        {
            var failingItemNames = new List<string>();

            foreach(var item in ProductionRepo.All.Where(a=>a.Schematic!=null))
            {
                try
                {
                    IIndustryJob job = PIJobModelManager.BuildPIProductionJobFor(item, new PIModelOptions() { MinimumManufacturingLevel = PIManufacturingLevel.Resource }, ProductionRepo, null);
                }
                catch (ItemNotHandledException)
                {
                    failingItemNames.Add(item.TypeName);
                }
            }

            if (failingItemNames.Count > 0)
            {
                var sb = new StringBuilder("Following items failed to create: ");

                foreach (var itemName in failingItemNames)
                    sb.AppendFormat("{0},", itemName);

                Assert.Fail(sb.ToString());
            }
        }

        public void CanChange_PI_ManufacturingChain()
        {
            dynamic robotics = ProductionRepo.Get(9848);

            IJobModelManager piModelManager = new PIJobModelManager(ProductionRepo, robotics,
                                                                                  new PIModelOptions
                                                                                      {MinimumManufacturingLevel = PIManufacturingLevel.Resource});

            IIndustryJob chainTop = piModelManager.ManagedChains.FirstOrDefault() as IIndustryJob;


            chainTop.Output.Quantity.Total = 6;

            Assert.AreEqual(2, chainTop.Runs);

            // check child steps
        }

        [TestMethod]
        [Ignore]
        public void CanBuild_Tech1_ManufacturingChain()
        {
        }

        [TestMethod]
        [Ignore]
        public void CanBuild_Tech2_ManufacturingChain()
        {
        }
    }
}